package thienphuc.patient.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;

import thienphuc.patient.util.HibernateUtil;

public abstract class AbstractHibernateDaoSupport<C, ID extends Serializable> implements Dao<C, ID> {

    private Class<C> classPersistent;

    public AbstractHibernateDaoSupport(Class<C> classPersistent) {
        super();
        this.classPersistent = classPersistent;
    }

    public Class<C> getClassPersistent() {
        return classPersistent;
    }

    /**
     * find by criteria
     */
    @SuppressWarnings("unchecked")
    protected final List<C> findByCriteria(Criterion... criterions) {
        Criteria crit = getSession().createCriteria(getClassPersistent());
        for (Criterion c : criterions) {
            crit.add(c);
        }

        return crit.list();
    }

    /**
     * find all object
     */
    public final List<C> findAll() {
        return findByCriteria();
    }

    /**
     * find by id
     */
    public final C findById(ID id) {
        C entity = null;

        try {
            entity = (C) getSession().get(getClassPersistent(), id);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return entity;
    }

    /**
     * find by class property
     */
    @SuppressWarnings("unchecked")
    public final List<C> findByProperty(String propertyName, Object value) {
        try {
            String queryString = "FROM " + classPersistent.getSimpleName()
                    + " AS Model WHERE Model." + propertyName
                    + "= :propertyVal";
            Query queryObject = getSession().createQuery(queryString);
            queryObject.setParameter("propertyVal", value);
            return queryObject.list();
        } catch (RuntimeException re) {
            re.printStackTrace();
            return new ArrayList<C>();
        }
    }

    /**
     * find by class property
     */
    @SuppressWarnings("unchecked")
    public final List<C> findByPropertyIgnoreCase(String propertyName,
            Object value) {
        try {
            String queryString = "FROM " + classPersistent.getSimpleName()
                    + " AS Model WHERE upper(Model." + propertyName
                    + ")= :propertyVal";
            Query queryObject = getSession().createQuery(queryString);
            value = ((String) value).toUpperCase();
            queryObject.setParameter("propertyVal", value);
            return queryObject.list();
        } catch (RuntimeException re) {
            re.printStackTrace();
            return new ArrayList<C>();
        }
    }

    /**
     * Save object to database
     */
    public final boolean save(C obj) {
        try {
            this.getSession().save(obj);
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    /**
     * update object to database
     */
    public final boolean update(C obj) {
        try {
            getSession().beginTransaction();
            this.getSession().update(obj);
            getSession().getTransaction().commit();
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    /**
     * delete object to database
     */
    public final boolean delete(C obj) {
        if (obj == null) {
            return false;
        }

        try {
            getSession().beginTransaction();
            this.getSession().delete(obj);
            getSession().getTransaction().commit();
            return true;
        } catch (Exception ex) {
            System.out.println("Delete error:" + ex.getMessage());
            return false;
        }
    }

    /**
     * merge with database
     */
    @SuppressWarnings("unchecked")
    public final C merge(C obj) {
        try {
            C result = (C) this.getSession().merge(obj);
            return result;
        } catch (RuntimeException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    /**
     * flush object to database
     */
    public final boolean flush() {
        try {
            this.getSession().flush();
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public final boolean close() {
        try {
            Session session = this.getSession();

            if (session != null) {
                session.flush();
                session.close();
            }

            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public Session getSession() {
        return HibernateUtil.getSession();
    }
}
